package de.westranger.forex.trading.genetic.allel.fitness;

import java.text.ParseException;

import de.westranger.forex.trading.genetic.ContentResolver;
import de.westranger.forex.trading.genetic.EvaluateOperator;
import de.westranger.forex.trading.genetic.allel.Node;
import de.westranger.forex.trading.genetic.allel.chromosom.Chromosom;
import de.westranger.forex.trading.genetic.allel.chromosom.CompositeChromosom;
import de.westranger.forex.trading.genetic.allel.operator.UnknownOperatorException;

public class UtilityMoneyManagedFitness implements ComputeFitness {
	private final ContentResolver conRes;
	private final int valueCount;
	private final EvaluateOperator eval;
	private final SignalType[] signal;
	private final double percentage;
	private final double money;
	private double stopLoss;
	private final double leverage;
	private double takeProfit;
	
	public UtilityMoneyManagedFitness(final ContentResolver conRes,final double money,final double percentage,final double leverage){
		this.conRes = conRes;
		
		this.valueCount = conRes.getValueCount();
		this.eval = new EvaluateOperator(conRes);
		this.signal = new SignalType[this.valueCount];	
		this.money = money;
		this.percentage = percentage;
		
		this.leverage = leverage;
	}

	@Override
	public CompositeFitness computeFitness(Chromosom chrom) throws UnknownOperatorException, ParseException{
		CompositeFitness comFit = null;
		if(chrom instanceof CompositeChromosom){
			this.takeProfit = ((CompositeChromosom) chrom).getTakeProfit();
			this.stopLoss = ((CompositeChromosom) chrom).getStoploss();
			comFit = this.computeFitness(((CompositeChromosom) chrom).getNodeSell(), ((CompositeChromosom) chrom).getNodeBuy());
		}
		return comFit;
	}

	public CompositeFitness computeFitness(final Node nodeSell,final Node nodeBuy) throws UnknownOperatorException, ParseException{
		final ProfitManager proMgr = new ProfitManager(conRes, this.leverage, this.percentage, this.stopLoss, this.money,this.takeProfit);

		double utilitySell = 0.5; // we prefer sell over by as default utility
		double utilityBuy = 0.0;

		this.eval.setTime(0);
		signal[0] = calcSignal(this.eval.evaluate(nodeBuy),this.eval.evaluate(nodeSell),SignalType.NOTHING,utilitySell,utilityBuy);
		
		double profit = this.money;
		int transStart = this.signal[0] != SignalType.NOTHING ? 0 : -1;
		double posTradeSell = 0;
		double negTradeSell = 0;
		double posTradeBuy = 0;
		double negTradeBuy = 0;
		double avgNegProfitSell = 0.0;
		double avgPosProfitSell = 0.0;
		double avgNegProfitBuy = 0.0;
		double avgPosProfitBuy = 0.0;
		double tmp = 0.0;

		for(int i=1;i<this.valueCount;i++){
			this.eval.setTime(i);
			signal[i] = calcSignal(this.eval.evaluate(nodeBuy),this.eval.evaluate(nodeSell),signal[i-1],utilitySell,utilityBuy);

			if(signal[i] != signal[i-1]){
				if(signal[i-1] == SignalType.BUY){
					tmp = proMgr.pendingBuyOrder(transStart, i);

					if(tmp > 0.0){
						posTradeBuy++;
						avgPosProfitBuy += tmp;
					}else{
						negTradeBuy++;
						avgNegProfitBuy += tmp;
					}

					//System.out.println(" buy von\t" + transStart  +"\tbis\t" + i + "\t" + tmp);
				}else if(signal[i-1] == SignalType.SELL){
					tmp = proMgr.pendingSellOrder(transStart, i);
					
					if(tmp > 0.0){
						posTradeSell++;
						avgPosProfitSell += tmp;
					}else{
						negTradeSell++;
						avgNegProfitSell += tmp;
					}

					//System.out.println(" sell von\t" + transStart  +"\tbis\t" + i + "\t" + tmp);
				}else{
					tmp = 0.0;
				}
				profit += tmp;
				
				if(profit < 0.000001){ // is this possible ?
					profit = 0.000001;
					break; // this is it, we dont have any money left
				}

				transStart = this.signal[i] != SignalType.NOTHING ? i : -1;
			}
			// now we will update the signal probabilities and utilities
			utilitySell = ((posTradeSell / (posTradeSell + negTradeSell)) * avgPosProfitSell)
					+ ((negTradeSell / (posTradeSell + negTradeSell)) * avgNegProfitSell);
			utilityBuy = ((posTradeBuy / (posTradeBuy + negTradeBuy)) * avgPosProfitBuy)
					+ ((negTradeBuy / (posTradeBuy + negTradeBuy)) * avgNegProfitBuy);

			utilitySell = Double.isNaN(utilitySell) || Double.isInfinite(utilitySell) ? 0.0 : utilitySell;
			utilityBuy = Double.isNaN(utilityBuy) || Double.isInfinite(utilityBuy) ? 0.0 : utilityBuy;
		}

//		profit -= this.money;
		
		if(profit <= 0.000001){
			profit = 0.000001;
		}

		double avgPosProfit = (avgPosProfitSell+avgPosProfitBuy)/(posTradeSell+posTradeBuy);
		double avgNegProfit = (avgNegProfitSell+avgNegProfitBuy)/(negTradeSell+negTradeBuy);

		// we penalize everything which does not trade
		if (Double.isNaN(avgPosProfit)) {
			avgPosProfit = 0.0;
		}

		if (Double.isNaN(avgNegProfit)) {
			avgNegProfit = 0.0;
		}

		return new CompositeFitness(profit,avgPosProfit,avgNegProfit,posTradeSell+posTradeBuy,negTradeSell+negTradeBuy,proMgr.getCounterStopLoss(),proMgr.getCounterTakeProfit(),posTradeBuy+negTradeBuy,posTradeSell+negTradeSell);
	}

	
	private SignalType calcSignal(final boolean sigBuy, final boolean sigSell,final SignalType sigPrev,final double utilitySell, final double utilityBuy) {
		SignalType result;

			if(sigBuy && sigSell){
				if(utilitySell >= utilityBuy){
					result = SignalType.SELL;	
				}else{
					result = SignalType.BUY;
				}
			}else if(sigSell){
				result = SignalType.SELL;
			}else if(sigBuy){
				result = SignalType.BUY;
			}else{
				result = SignalType.NOTHING; 	
			}

		return result;
	}

}
